﻿// 功能：Google Static Map View
// 描述：
// 作者：温伟鹏
// 日期：2010/10/22 11:37:01

using System;
using System.Collections.Generic;
using System.Text;
using MeizuSDK.Presentation;
using System.Drawing;
using System.ComponentModel;
using System.IO;
using System.Security.Cryptography;

namespace GoogleMaps.StaticMap
{
    /// <summary>
    /// Google Static Map View
    /// </summary>
    public class MapView : PictureBox
    {
        #region 常量
        /// <summary>
        /// 地图最大宽度
        /// </summary>
        private const int MaxWidth = 640;
        /// <summary>
        /// 地图最大高度
        /// </summary>
        private const int MaxHeight = 640;
        /// <summary>
        /// 默认的缩放级别
        /// </summary>
        private const byte DefaultZoomLevel = 16;
        /// <summary>
        /// 最小缩放级别
        /// </summary>
        private const byte MinZoomLevel = 0;
        /// <summary>
        /// 最大的缩放级别
        /// </summary>
        private const byte MaxZoomLevel = 21;
        /// <summary>
        /// 用来获取地图图片的地址格式化字符串
        /// </summary>
        private const string GStaticMapUrlFormat = "http://maps.google.com/maps/api/staticmap?{0}";
        /// <summary>
        /// 默认的地图图片缓存目录
        /// </summary>
        private const string DefaultCacheFolder = @"\Disk\Temp\GoogleStaticMapCache";
        #endregion

        #region 事件
        /// <summary>
        /// 当地图视图中的属性被修改后，触发此事件
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region 私有变量
        /// <summary>
        /// 指定地图类型
        /// </summary>
        private MapType mapType = MapType.RoadMap;
        /// <summary>
        /// 地图中心点。如果不设置地图显示的标记点，则此属性必须设置。
        /// </summary>
        private GeoPoint centerPoint;
        /// <summary>
        /// 地图缩放级别。如果不设置地图显示的标记点，则此属性必须设置。
        /// </summary>
        private byte zoomLevel = DefaultZoomLevel;
        /// <summary>
        /// 地图图片格式。默认为PNG8
        /// </summary>
        private MapImageFormat imgFormat = MapImageFormat.PNG8;
        /// <summary>
        /// 是否在移动设备上显示图像。默认false
        /// </summary>
        private bool mobile;
        /// <summary>
        /// 显示在地图上的标记。如果一个标记中指定多个地址，则会显示多个相同样式的标记。如果要显示不同样式的标记，则需要添加多个标记
        /// </summary>
        private List<Marker> markers;
        /// <summary>
        /// 显示在地图上的路径。
        /// </summary>
        private List<MapPath> pathList;
        /// <summary>
        /// 地图图片缓存目录
        /// </summary>
        private string cacheFolder;
        /// <summary>
        /// 是否加载过地图。
        /// </summary>
        private bool isLoadedMap;
        #endregion

        #region 公共属性
        /// <summary>
        /// 获取或设置地图图片的默认缓存目录
        /// </summary>
        public string CacheFolder
        {
            get
            {
                if (String.IsNullOrEmpty(cacheFolder))
                    this.cacheFolder = DefaultCacheFolder;
                return this.cacheFolder;
            }
            set
            {
                if (value != this.cacheFolder)
                {
                    this.cacheFolder = value;
                }
            }
        }
        /// <summary>
        /// 获取或设置地图视图的宽度
        /// </summary>
        public override int Width
        {
            get
            {
                return base.Width;
            }
            set
            {
                base.Width = Math.Max(0, Math.Min(MaxWidth, value));

                // 引发事件
                OnPropertyChanged(new PropertyChangedEventArgs("Width"));
            }
        }
        /// <summary>
        /// 获取或设置地图视图的高度
        /// </summary>
        public override int Height
        {
            get
            {
                return base.Height;
            }
            set
            {
                base.Height = Math.Max(0, Math.Min(MaxHeight, value));

                // 引发事件
                OnPropertyChanged(new PropertyChangedEventArgs("Height"));
            }
        }
        /// <summary>
        /// 获取或设置地图视图的大小
        /// </summary>
        public override Size Size
        {
            get
            {
                return base.Size;
            }
            set
            {
                base.Size = new Size(Math.Max(0, Math.Min(MaxWidth, value.Width)), Math.Max(0, Math.Min(MaxHeight, value.Height)));

                // 引发事件
                OnPropertyChanged(new PropertyChangedEventArgs("Size"));
            }
        }
        /// <summary>
        /// 获取或设置地图显示类型。如：标准路线图、卫星图等
        /// </summary>
        public MapType MapType
        {
            get
            {
                return this.mapType;
            }
            set
            {
                if (value != this.mapType)
                {
                    this.mapType = value;
                    // 引发事件
                    OnPropertyChanged(new PropertyChangedEventArgs("MapType"));
                }
            }
        }
        /// <summary>
        /// 获取或设置一个值，指示设备是否使用传感器确定用户位置。默认为false
        /// </summary>
        public bool UseSensor
        {
            get;
            set;
        }
        /// <summary>
        /// 获取或设置地图中心点
        /// </summary>
        public GeoPoint Center
        {
            get
            {
                return this.centerPoint;
            }
            set
            {
                if (value != this.centerPoint)
                {
                    this.centerPoint = value;
                    // 引发事件
                    OnPropertyChanged(new PropertyChangedEventArgs("Center"));
                }
            }
        }
        /// <summary>
        /// 获取或设置地图的缩放级别
        /// </summary>
        public byte ZoomLevel
        {
            get
            {
                return this.zoomLevel;
            }
            set
            {
                if (value != this.zoomLevel)
                {
                    this.zoomLevel = Math.Max(MinZoomLevel, Math.Min(MaxZoomLevel, value));
                    // 引发事件
                    OnPropertyChanged(new PropertyChangedEventArgs("ZoomLevel"));
                }
            }
        }
        /// <summary>
        /// 获取或设置地图图片格式
        /// </summary>
        public MapImageFormat ImageFormat
        {
            get
            {
                return this.imgFormat;
            }
            set
            {
                if (value != this.imgFormat)
                {
                    this.imgFormat = value;
                    // 引发事件
                    OnPropertyChanged(new PropertyChangedEventArgs("ImageFormat"));
                }
            }
        }
        /// <summary>
        /// 获取或设置一个值，指示控件是否下载用于移动设备的地图图片。
        /// 这种图片格式效果较差，适用于小屏幕的移动设备观看。
        /// </summary>
        public bool UseMobileMap
        {
            get
            {
                return this.mobile;
            }
            set
            {
                if (value != this.mobile)
                {
                    this.mobile = value;
                    // 引发事件
                    OnPropertyChanged(new PropertyChangedEventArgs("UseMobileMap"));
                }
            }
        }
        #endregion

        #region 构造函数
        /// <summary>
        /// 构造地图视图实例
        /// </summary>
        public MapView()
        {
            markers = new List<Marker>();
            pathList = new List<MapPath>();
        }
        #endregion

        #region 函数
        /// <summary>
        /// 放大地图
        /// </summary>
        public void ZoomIn()
        {
            ZoomLevel++;
        }
        /// <summary>
        /// 缩小地图
        /// </summary>
        public void ZoomOut()
        {
            ZoomLevel--;
        }
        /// <summary>
        /// 向地图中添加标记
        /// </summary>
        /// <param name="marker">显示在地图上的标记</param>
        public void AddMarker(Marker marker)
        {
            if (marker != null)
            {
                this.markers.Add(marker);
            }
        }
        /// <summary>
        /// 向地图中添加要显示的路径
        /// </summary>
        /// <param name="path">显示在地图上的路径</param>
        public void AddPath(MapPath path)
        {
            if (path != null)
            {
                this.pathList.Add(path);
            }
        }
        /// <summary>
        /// 生成可以下载地图图片的地址。
        /// </summary>
        /// <returns></returns>
        private string GenerateMapImageURL()
        {
            StringBuilder parameters = new StringBuilder();
            // 中心点
            if (Center != null)
            {
                parameters.Append(string.Format("center={0}&", Center.ToString()));
            }
            // 缩放级别
            parameters.Append(string.Format("zoom={0}&", ZoomLevel));
            // 地图大小
            parameters.Append(string.Format("size={0}x{1}&", Width, Height));
            // 图像格式
            if (ImageFormat != MapImageFormat.PNG8)
            {
                parameters.Append(string.Format("format={0}&", ImageFormat.ToString().ToLower()));
            }
            // 地图类型
            if (MapType != MapType.RoadMap)
            {
                parameters.Append(string.Format("maptype={0}&", MapType.ToString().ToLower()));
            }
            // 是否下载用于移动设备的图片
            parameters.Append(string.Format("mobile={0}&", UseMobileMap.ToString().ToLower()));
            // 传感器
            parameters.Append(string.Format("sensor={0}", UseSensor.ToString().ToLower()));
            // 语言

            // 标记
            for (int i = 0; i < markers.Count; i++)
            {
                if (!markers[i].IsEmpty)
                    parameters.Append("&" + markers[i].ToString());
            }

            // 路径
            for (int i = 0; i < pathList.Count; i++)
            {
                if (!pathList[i].IsEmpty)
                    parameters.Append("&" + pathList[i].ToString());
            }

            // TODO：增加视口支持。（保持可见的位置）

            return string.Format(GStaticMapUrlFormat, parameters.ToString());
        }
        /// <summary>
        /// 根据地图图片所在URL获取地图图片在本地的文件名
        /// </summary>
        /// <param name="mapImgUrl">地图URL</param>
        /// <returns></returns>
        private string GetSavedMapImageFileName(string mapImgUrl)
        {
            if (String.IsNullOrEmpty(mapImgUrl))
                return string.Empty;

            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();

            string result = BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(mapImgUrl)));

            switch (ImageFormat)
            {
                case MapImageFormat.PNG8:
                case MapImageFormat.PNG32:
                    {
                        result += ".png";
                    }
                    break;
                case MapImageFormat.JPG:
                case MapImageFormat.JPG_Baseline:
                    {
                        result += ".jpg";
                    }
                    break;
                case MapImageFormat.GIF:
                    {
                        result += ".gif";
                    }
                    break;
            }

            return result;
        }
        /// <summary>
        /// 引发 PropertyChanged 事件
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (isLoadedMap)
                RefreshView();

            if (PropertyChanged != null)
            {
                PropertyChanged(this, e);
            }
        }
        /// <summary>
        /// 刷新视图
        /// </summary>
        private void RefreshView()
        {
            string imgUrl = GenerateMapImageURL();

            string imgFile = GetSavedMapImageFileName(imgUrl);

            string cachedImgFile = Path.Combine(CacheFolder, imgFile);

            if (File.Exists(cachedImgFile))
            {
                LoadFromFile(cachedImgFile);
            }
            else
            {
                int errorRepeatedCount = 3;

                for (int i = 0; i < errorRepeatedCount; i++)
                {
                    try
                    {
                        if (MapDownloader.DownloadFromUrl(cachedImgFile, imgUrl))
                        {
                            LoadFromFile(cachedImgFile);
                            break;
                        }
                        else
                        {
                            if (i == errorRepeatedCount - 1)
                            {
                                MessageBox.Show(OwnerForm, "地图下载失败！", "错误");
                            }
                        }
                    }
                    catch (System.Exception ex)
                    {
                        MessageBox.Show(OwnerForm, ex.Message, "错误");
                    }
                }
            }
        }

        /// <summary>
        /// 地图控件设置完毕后，调用此函数加载地图显示。
        /// </summary>
        public void LoadMap()
        {
            if (!isLoadedMap)
                isLoadedMap = true;

            RefreshView();
        }
        #endregion
    }
}
